﻿# include "vd.h"
# include "VD_Crypto.h"

#pragma code_seg("PAGE")

NTSTATUS VD_OpenFile (
    IN PDEVICE_OBJECT   DeviceObject,
    IN PIRP             Irp
    )
{
 
    PDEVICE_EXTENSION               device_extension;
    PCREATE_DISK_INFO				create_disk_info;
    LARGE_INTEGER					LI_FileSize;
	NTSTATUS                        status;
    
	OBJECT_ATTRIBUTES               object_attributes;
    FILE_END_OF_FILE_INFORMATION    file_eof;
    FILE_BASIC_INFORMATION          file_basic;
    FILE_STANDARD_INFORMATION       file_standard;
    FILE_ALIGNMENT_INFORMATION      file_alignment;

	OBJECT_ATTRIBUTES				reg_object_attributes;
	ULONG							Disposition;
	WCHAR							reg_device_name[MAXIMUM_FILENAME_LENGTH];
	USHORT							FileNameOffset = 4;	
	LONG							CryptedPasswordLen = 0;
	PIO_STACK_LOCATION				io_stack;
	CHAR							Disk_ID[16];	
	PULONG							Crypted_Disk_ID;
	ULONG							FirstValue;	
	USHORT							i;
	PVOID							RetVal;
			
	DbgPrint("VD_OpenFile \n");
	
    device_extension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
	
	RetVal = Irp->AssociatedIrp.SystemBuffer;

	io_stack = IoGetCurrentIrpStackLocation(Irp);

    create_disk_info = (PCREATE_DISK_INFO) Irp->AssociatedIrp.SystemBuffer;
  
	LI_FileSize.QuadPart = (create_disk_info->FileSize + PassOffset);	

    InitializeObjectAttributes(
							 &object_attributes,
							 &device_extension->file_name,
							 OBJ_CASE_INSENSITIVE| OBJ_KERNEL_HANDLE,
							 NULL,
							 NULL
							  );

    status = ZwCreateFile(
						&device_extension->file_handle,
						GENERIC_READ | GENERIC_WRITE,
						&object_attributes,
						&Irp->IoStatus,
						NULL,
						FILE_ATTRIBUTE_NORMAL,
						0,
						FILE_OPEN,
						FILE_NON_DIRECTORY_FILE |
						FILE_RANDOM_ACCESS |
						FILE_NO_INTERMEDIATE_BUFFERING |
						//FILE_WRITE_THROUGH |
						FILE_SYNCHRONOUS_IO_NONALERT,
						NULL,
						0
						 );

    if (status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_NO_SUCH_FILE)
    {

		status = ZwCreateFile(
							&device_extension->file_handle,
							GENERIC_READ | GENERIC_WRITE,
							&object_attributes,
							&Irp->IoStatus,
							&LI_FileSize,
							FILE_ATTRIBUTE_NORMAL,
							0,
							FILE_OPEN_IF,
							FILE_NON_DIRECTORY_FILE |
							FILE_RANDOM_ACCESS |
							FILE_NO_INTERMEDIATE_BUFFERING |
							//FILE_WRITE_THROUGH |
							FILE_SYNCHRONOUS_IO_NONALERT,
							NULL,
							0
							 );

		if (!NT_SUCCESS(status))
        {
			DbgPrint ("Unable to Open existing File\n");
			ExFreePoolWithTag(device_extension->file_name.Buffer,POOL_TAG);
            return status;
        }
	
        if (Irp->IoStatus.Information == FILE_CREATED)
        {
			status = ZwFsControlFile(
									device_extension->file_handle,
									NULL,
									NULL,
									NULL,
									&Irp->IoStatus,
									FSCTL_SET_SPARSE,
									NULL,
									0,
									NULL,
									0
									);

		if (NT_SUCCESS(status))
        {
			DbgPrint("File created\n");
        }

        file_eof.EndOfFile.QuadPart = create_disk_info->FileSize;

        status = ZwSetInformationFile(
									device_extension->file_handle,
									&Irp->IoStatus,
									&file_eof,
									sizeof(FILE_END_OF_FILE_INFORMATION),
									FileEndOfFileInformation
									 );

         if (!NT_SUCCESS(status))
         {
			DbgPrint ("FILE_END_OF_FILE_INFORMATION Failure\n");
			ExFreePoolWithTag(device_extension->file_name.Buffer,POOL_TAG);
            ZwClose(device_extension->file_handle);
            return status;
		 }
            
        }
   
	}
    else if (!NT_SUCCESS(status))
    {
        DbgPrint ("Cann't create File\n");
		ExFreePoolWithTag(device_extension->file_name.Buffer,POOL_TAG);
        return status;
    }

	status = ZwQueryInformationFile(
								  device_extension->file_handle,
								  &Irp->IoStatus,
								  &file_standard,
								  sizeof(FILE_STANDARD_INFORMATION),
								  FileStandardInformation
								   );

    if (!NT_SUCCESS(status))
    {
        DbgPrint ("Cann't get FILE_STANDARD_INFORMATION\n");
		ExFreePoolWithTag(device_extension->file_name.Buffer,POOL_TAG);
        ZwClose(device_extension->file_handle);
        return status;
    }
	

	device_extension->file_size = file_standard.EndOfFile;

    status = ZwQueryInformationFile(
								  device_extension->file_handle,
								  &Irp->IoStatus,
								  &file_basic,
								  sizeof(FILE_BASIC_INFORMATION),
								  FileBasicInformation
								   );

    if (!NT_SUCCESS(status))
    {
        DbgPrint ("Cann't get FILE_BASIC_INFORMATION\n");
		ExFreePoolWithTag(device_extension->file_name.Buffer,POOL_TAG);
        ZwClose(device_extension->file_handle);
        return status;
    }

    status = ZwQueryInformationFile(
								  device_extension->file_handle,
								  &Irp->IoStatus,
								  &file_alignment,
								  sizeof(FILE_ALIGNMENT_INFORMATION),
								  FileAlignmentInformation
									);

    if (!NT_SUCCESS(status))
    {
        DbgPrint ("Cann't get FILE_ALIGNMENT_INFORMATION\n");
		ExFreePoolWithTag(device_extension->file_name.Buffer,POOL_TAG);
        ZwClose(device_extension->file_handle);
        return status;
    }


	DeviceObject->AlignmentRequirement = file_alignment.AlignmentRequirement;
    DeviceObject->Characteristics &= ~FILE_READ_ONLY_DEVICE;
	device_extension->disk_mounted = TRUE;
	device_extension->PasswordCheck = 0;
	

	status = ZwReadFile(
				device_extension->file_handle,
				NULL,
				NULL,
				NULL,
				&Irp->IoStatus,
				&FirstValue,
				LONGSIZE,
				NULL,
				NULL
               );
	if (!NT_SUCCESS(status)){
		DbgPrint("Unable to read First value length  \n");	
		ExFreePoolWithTag(device_extension->file_name.Buffer,POOL_TAG);
		ZwClose(device_extension->file_handle);
		Irp->IoStatus.Status = status;
		Irp->IoStatus.Information = LONGSIZE;
		return status;
	}

	
	VD_BF_Init (&device_extension->ctx, CryptKey, CryptKeyLen);	
	DbgPrint("First Value %d \n",FirstValue);
	if (FirstValue != 0)
	{
		io_stack->Parameters.Read.ByteOffset.QuadPart = 0i16;
		Crypted_Disk_ID = (PULONG) ExAllocatePoolWithTag(
														PagedPool,
														DiskIdentificationLen + LONGSIZE,		
														POOL_TAG
														);
		if (Crypted_Disk_ID == NULL)
		{
			DbgPrint("Unable to allocate memory for Crypted_Disk_ID \n");	
			ExFreePoolWithTag(device_extension->file_name.Buffer,POOL_TAG);
			ZwClose(device_extension->file_handle);
			Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
			Irp->IoStatus.Information = 0;
			return STATUS_INSUFFICIENT_RESOURCES;
		}
	
		status = ZwReadFile(
						device_extension->file_handle,
						NULL,
						NULL,
						NULL,
						&Irp->IoStatus,
						Crypted_Disk_ID,
						DiskIdentificationLen,
						&io_stack->Parameters.Read.ByteOffset,
						NULL
							);

		if (!NT_SUCCESS(status))
		{
			DbgPrint("Unable to read read Disk ID string \n");	
			ExFreePoolWithTag(device_extension->file_name.Buffer,POOL_TAG);
			ZwClose(device_extension->file_handle);
			Irp->IoStatus.Status = status;
			Irp->IoStatus.Information = 0;
			return status;
		}

		for (i = 0; i < DiskIdentificationLen/LONGSIZE;i+=2)
		{
			VD_BF_Decrypt(&device_extension->ctx,&Crypted_Disk_ID[i],&Crypted_Disk_ID[i+1]);
		}

		RtlCopyMemory(
					Disk_ID,
					Crypted_Disk_ID,
					DiskIdentificationLen
					);		
							
		Disk_ID[DiskIdentificationLen]= '\0';
		DbgPrint("Disk_ID %s \n",Disk_ID);
	
		ExFreePoolWithTag(Crypted_Disk_ID,POOL_TAG);

		device_extension->DiskIDCheck = RtlEqualMemory(
													Disk_ID, 
													DiskIdentification,
													DiskIdentificationLen
													  );
		if (!device_extension->DiskIDCheck)
		{
		
			
			DbgPrint("This is not MultiVDD Disk \n");
			RtlCopyMemory(RetVal,&device_extension->DiskIDCheck,LONGSIZE);

			ExFreePoolWithTag(device_extension->file_name.Buffer,POOL_TAG);
			ZwClose(device_extension->file_handle);
			Irp->IoStatus.Status = STATUS_FILE_INVALID;
			Irp->IoStatus.Information = LONGSIZE;
			return STATUS_FILE_INVALID;
		}

		io_stack->Parameters.Read.ByteOffset.QuadPart = DiskIdentificationLen;
		status = ZwReadFile(
						device_extension->file_handle,
						NULL,
						NULL,
						NULL,
						&Irp->IoStatus,
						&CryptedPasswordLen,
						LONGSIZE,
						&io_stack->Parameters.Read.ByteOffset,
						NULL
					  );

		if (!NT_SUCCESS(status))
		{
			DbgPrint("Unable to read read password len \n");
			ExFreePoolWithTag(device_extension->file_name.Buffer,POOL_TAG);
			ZwClose(device_extension->file_handle);
			Irp->IoStatus.Status = status;
			Irp->IoStatus.Information = 0;
			return status;
		}
		if (CryptedPasswordLen == 0)
		{
			DbgPrint("Password len is 0 \n");
			ExFreePoolWithTag(device_extension->file_name.Buffer,POOL_TAG);
			ZwClose(device_extension->file_handle);
			Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
			Irp->IoStatus.Information = 0;
			return STATUS_INVALID_PARAMETER;
		}

	}
	else{
		RtlCopyMemory(RetVal,&NEWDISK,LONGSIZE);
		}
	
	DbgPrint("Crypted password %d\n",CryptedPasswordLen);
	
	if (CryptedPasswordLen != 0)
		device_extension->enc_pass_len = CryptedPasswordLen;
	
	device_extension->IsDiskRaw = (FirstValue == 0) ? TRUE : FALSE;

	status = RtlStringCchPrintfW(
							reg_device_name, 
							sizeof(reg_device_name)/sizeof(reg_device_name[0]), 
							L"%ws%wc%u", 
							RegPathW.Buffer,
							REG_PATH_SUB_DIR,
							device_extension->DeviceNumber
							);

	if (!NT_SUCCESS(status)){
		DbgPrint("Can't Create Disk Reg Path");
		Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
		Irp->IoStatus.Information = 0;
		return STATUS_UNSUCCESSFUL;
	}

	RtlInitUnicodeString(&device_extension->RegPathName, reg_device_name);

	InitializeObjectAttributes(
							 &reg_object_attributes, 
							 &device_extension->RegPathName, 
							 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 
							 NULL, 
							 NULL
							 );

	status = ZwCreateKey(
					   &device_extension->RegPathHandle, 
					   KEY_ALL_ACCESS,	
					   &reg_object_attributes, 
					   0, 
					   NULL, 
					   REG_OPTION_VOLATILE, 
					  &Disposition
					   ); 

	if (!NT_SUCCESS(status))
	{
		DbgPrint("Disk Key was not created \n");
		ZwClose(device_extension->RegPathHandle);
		Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
		Irp->IoStatus.Information = 0;
		return STATUS_UNSUCCESSFUL;
	}
	
//Write DiskStatus to Register
	status =  RtlWriteRegistryValue(
								  RTL_REGISTRY_ABSOLUTE,
								  RegPathW.Buffer,
								  REG_DISK_DEVICE_STATUS,
								  REG_SZ,
								  DiskDeviceStatus,
								  (sizeof(DiskDeviceStatus))
									);

	if (!NT_SUCCESS(status)){
		DbgPrint("Disk status not written to register %x \n",status);
		Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
		Irp->IoStatus.Information = 0;
		return STATUS_UNSUCCESSFUL;
	}
	
//Write File Name to Register
	status =  RtlWriteRegistryValue(
								  RTL_REGISTRY_ABSOLUTE,
								  device_extension->RegPathName.Buffer,
							      REG_DEVICE_FILE_NAME,
								  REG_SZ,
								  (device_extension->file_name.Buffer + FileNameOffset),
								  (device_extension->file_name.Length - (FileNameOffset-1)*sizeof(WCHAR))
									);

	if (!NT_SUCCESS(status)){
		DbgPrint("Disk File Name not written to register %x \n",status);
		Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
		Irp->IoStatus.Information = 0;
		return STATUS_UNSUCCESSFUL;
	}
	
//Write File Size to Register
	status = RtlWriteRegistryValue(
								 RTL_REGISTRY_ABSOLUTE,
								 device_extension->RegPathName.Buffer,
								 REG_DEVICE_FILE_SIZE,
								 REG_QWORD,
								 &device_extension->file_size,
								 (sizeof(device_extension->file_size))
								 );

	if (!NT_SUCCESS(status)){
		DbgPrint("Disk File Size not written to register %x \n",status);
		Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
		Irp->IoStatus.Information = 0;
		return STATUS_UNSUCCESSFUL;
	}
	
	DbgPrint ("Finish Createing File RefCount %d \n", DeviceObject->ReferenceCount);
	Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = LONGSIZE;
    return STATUS_SUCCESS;
}

